package org.gendut.collection.mutable;

import org.gendut.func.Function;

public abstract class Transformer<S, T> {

  abstract public T value(S s);

  @SuppressWarnings("rawtypes")
  private final static Transformer identity = new Transformer() {
    public Object value(Object t) {
      return t;
    }
  };

  @SuppressWarnings("unchecked")
  public static <T> Transformer<T, T> ret() {
    return identity;
  }

  private static class BoundedTransition<S, T, R> extends Transformer<S, R> {

    final Transformer<S, T> first;
    final Function<T, Transformer<T, R>> fun;

    public BoundedTransition(Transformer<S, T> first,
        Function<T, Transformer<T, R>> fun) {

      this.first = first;
      this.fun = fun;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public R value(S s) {
      Object t = first.value(s);
      Transformer m = fun.get((T) t);
      while (m instanceof BoundedTransition) {
        BoundedTransition bm = (BoundedTransition) m;
        t = bm.first.value(t);
        m = (Transformer) bm.fun.get(t);
      }
      return (R) m.value(t);
    }
  }

  public <R> Transformer<S, R> bind(Function<T, Transformer<T, R>> f) {
    return new BoundedTransition<S, T, R>(this, f);
  }
}
